package io.halo.payment.modules.app.controller;

import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.wechat.pay.java.core.Config;
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
import com.wechat.pay.java.service.payments.jsapi.JsapiService;
import com.wechat.pay.java.service.payments.jsapi.model.Amount;
import com.wechat.pay.java.service.payments.jsapi.model.Payer;
import com.wechat.pay.java.service.payments.jsapi.model.PrepayRequest;
import com.wechat.pay.java.service.payments.jsapi.model.PrepayResponse;
import io.halo.payment.common.constants.HaloPaymentConstant;
import io.halo.payment.common.constants.WxConstant;
import io.halo.payment.common.enums.SignTypeEnum;
import io.halo.payment.common.enums.TradeTypeEnum;
import io.halo.payment.common.utils.R;
import io.halo.payment.common.validator.ValidatorUtils;
import io.halo.payment.github.wxpay.sdk.HaloPaymentConfig;
import io.halo.payment.github.wxpay.sdk.WXPay;
import io.halo.payment.github.wxpay.sdk.WXPayUtil;
import io.halo.payment.modules.app.annotation.Login;
import io.halo.payment.modules.app.form.PayOrderForm;
import io.halo.payment.modules.app.form.WxLoginForm;
import io.halo.payment.modules.app.model.HaloPaymentOrder;
import io.halo.payment.modules.app.model.HaloPaymentUser;
import io.halo.payment.modules.app.service.HaloPaymentOrderService;
import io.halo.payment.modules.app.service.HaloPaymentUserService;
import io.halo.payment.modules.app.utils.JwtUtils;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.math.BigDecimal;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * 微信登录授权
 *
 * @author austin
 */
@Slf4j
@RestController
@RequestMapping("/app/wx")
@Api("微信相关接口")
public class WeiXinController {

    @Value("${halo-pay.application.app-id}")
    private String appId;

    @Value("${halo-pay.application.app-secret}")
    private String appSecret;

    @Value("halo-pay.application.mch-key")
    private String mchKey;

    @Value("halo-pay.application.key-path")
    private String keyPath;

    @Autowired
    private HaloPaymentUserService haloPaymentUserService;
    @Autowired
    private HaloPaymentOrderService haloPaymentOrderService;
    @Autowired
    private JwtUtils jwtUtils;

    /**
     * 注入微信相关配置
     */
    @Autowired
    private HaloPaymentConfig haloPaymentConfig;

    /**
     * 登录，如果用户未存在，注册用户
     * <p>
     * 小程序登录调用示例：https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/user-login/code2Session.html#%E8%B0%83%E7%94%A8%E7%A4%BA%E4%BE%8B
     */
    @PostMapping("login")
    @ApiOperation("登录")
    public R login(@RequestBody WxLoginForm form) {

        // 表单校验
        ValidatorUtils.validateEntity(form);
        HashMap<String, Object> params = new HashMap<>();
        params.put("appid", appId);
        params.put("secret", appSecret);
        params.put("js_code", form.getCode());
        params.put("grant_type", "authorization_code");
        String response = HttpUtil.get(WxConstant.JS_CODE2SESSION, params);

        JSONObject jsonObject = JSONUtil.parseObj(response);
        // 获取返回用户唯一标识
        String openId = jsonObject.getStr("openid");
        if (StringUtils.isBlank(openId)) {
            return R.error("临时登陆凭证错误");
        }
        HaloPaymentUser user = new HaloPaymentUser();
        user.setOpenId(openId);
        QueryWrapper<HaloPaymentUser> queryWrapper = new QueryWrapper<>(user);
        int count = haloPaymentUserService.count(queryWrapper);
        if (count == 0) {
            user.setNickname(form.getNickname());
            user.setAvatarUrl(form.getAvatarUrl());
            user.setType(2);
            user.setCreateTime(new Date());
            haloPaymentUserService.save(user);
        }
        user = new HaloPaymentUser();
        user.setOpenId(openId);
        queryWrapper = new QueryWrapper<>(user);
        user = haloPaymentUserService.getOne(queryWrapper);

        // generate token
        String token = jwtUtils.generateToken(user.getUserId());
        HashMap<String, Object> resultMap = new HashMap<>();
        resultMap.put("token", token);
        resultMap.put("expire", jwtUtils.getExpire());

        return R.ok(resultMap);
    }

    @Login
    @PostMapping(value = "/creatMicroAppOrder")
    @ApiOperation(value = "小程序付款")
    public R creatMicroAppOrder(@RequestBody PayOrderForm form, @RequestHeader HashMap header) {

        R result = new R();

        ValidatorUtils.validateEntity(form);
        String token = String.valueOf(header.get("token"));
        Long userId = Long.valueOf(jwtUtils.getClaimByToken(token).getSubject());

        HaloPaymentUser queryUserEntity = new HaloPaymentUser();
        queryUserEntity.setUserId(userId);
        QueryWrapper<HaloPaymentUser> queryWrapper = new QueryWrapper<>(queryUserEntity);
        HaloPaymentUser user = haloPaymentUserService.getOne(queryWrapper);
        if (user == null) {
            return R.error("用户不存在!");
        }
        HaloPaymentOrder queryOrderEntity = new HaloPaymentOrder();
        queryOrderEntity.setId(form.getOrderId());
        queryOrderEntity.setUserId(user.getUserId());
        QueryWrapper<HaloPaymentOrder> orderQueryWrapper = new QueryWrapper<>(queryOrderEntity);
        HaloPaymentOrder order = haloPaymentOrderService.getOne(orderQueryWrapper);
        if (order == null) {
            return R.error("该用户订单不存在!");
        }

        //向微信平台发出请求，创建支付订单，调用统一下单接口：https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1
        String amount = order.getAmount().multiply(new BigDecimal("100")).intValue() + "";        //订单总金额：单位分，元转分
        try {
            //注入微信相关配置
            WXPay wxPay = new WXPay(haloPaymentConfig);
            Map<String, String> params = new HashMap<>(16);
            params.put(WxConstant.BODY, "订单备注");
            params.put(WxConstant.NONCE_STR, WXPayUtil.generateNonceStr());          //随机字符串
            params.put(WxConstant.OUT_TRADE_NO, order.getCode());                    //商户系统内部订单号(这里也就是订单编号)
            params.put(WxConstant.TOTAL_FEE, amount);                                //订单总金额，单位为分 (字符串类型)
            params.put(WxConstant.SPBILL_CREATE_IP, "127.0.0.1");                    //终端IP
            params.put(WxConstant.NOTIFY_URL, "http://127.0.0.1/app/pay/notify");    //异步接收微信支付结果通知的回调地址
            params.put(WxConstant.TRADE_TYPE, TradeTypeEnum.JSAPI.getTradeType());   //交易类型
            params.put(WxConstant.OPENID, user.getOpenId());                         //用户授权唯一标识

            //统一下单接口，商户系统需要先调用该接口在微信支付后台生成预支付交易单(签名不对，这里会返回签名错误，无法拿到prepayId，实际场景需要获取正确的数据签名，这里的模拟的)
            Map<String, String> response = wxPay.unifiedOrder(params);
            String flag = response.get("return_code");
            String prepayId = response.get("prepay_id");
            if (!WxConstant.FAIL.equals(flag) && StringUtils.isNotBlank(prepayId)) {
                log.info("prepay_id: {}", prepayId);
                order.setPrepayId(prepayId);
                UpdateWrapper<HaloPaymentOrder> updateWrapper = new UpdateWrapper<>();
                updateWrapper.set("id", order.getId());
                updateWrapper.set("prepay_id", prepayId);
                haloPaymentOrderService.update(order, updateWrapper);

                // 生成数字签名等信息返回给前端
                Map<String, String> data = new HashMap<>(16);
                String timeStamp = System.currentTimeMillis() + "";
                String nonceStr = WXPayUtil.generateNonceStr();
                data.put(WxConstant.APPID, appId);                                //appId
                data.put(WxConstant.TIMESTAMP, timeStamp);                        //时间戳
                data.put(WxConstant.NONCE_STR, WXPayUtil.generateNonceStr());     //随机字符串
                data.put(WxConstant.PACKAGE, prepayId);                           //预支付交易会话标识
                data.put(WxConstant.SIGN_TYPE, SignTypeEnum.MD5.getType());       //数字签名方式，默认为MD5
                //生成数字签名
                String paySign = WXPayUtil.generateSignature(data, mchKey);

                result = R.ok().put(WxConstant.PACKAGE, WxConstant.PREPAY_ID + "=" + prepayId)
                        .put(WxConstant.TIMESTAMP, timeStamp)
                        .put(WxConstant.NONCE_STR, nonceStr)
                        .put(WxConstant.PAY_SIGN, paySign);
            } else {
                String errorMsg = String.format("prepay_id获取失败, %s", result.get("return_msg"));
                result = R.error(errorMsg);
            }
        } catch (Exception e) {
            e.printStackTrace();
            return R.error("微信支付统一下单支付异常!");
        }
        return result;
    }


    @Login
    @PostMapping(value = "/creatMicroAppOrderV3")
    @ApiOperation(value = "小程序付款V3版本")
    public R creatMicroAppOrderV3(@RequestBody PayOrderForm form, @RequestHeader HashMap header) {
        ValidatorUtils.validateEntity(form);
        String token = String.valueOf(header.get("token"));
        Long userId = Long.valueOf(jwtUtils.getClaimByToken(token).getSubject());

        HaloPaymentUser user = new HaloPaymentUser();
        user.setUserId(userId);
        QueryWrapper<HaloPaymentUser> queryWrapper = new QueryWrapper<>(user);
        HaloPaymentUser one = haloPaymentUserService.getOne(queryWrapper);
        if (one == null) {
            return R.error("用户不存在!");
        }
        HaloPaymentOrder order = new HaloPaymentOrder();
        order.setId(form.getOrderId());
        order.setUserId(one.getUserId());
        QueryWrapper<HaloPaymentOrder> orderQueryWrapper = new QueryWrapper<>(order);
        HaloPaymentOrder exist = haloPaymentOrderService.getOne(orderQueryWrapper);
        if (exist == null) {
            return R.error("该用户订单不存在!");
        }
        String totalAmount = order.getAmount().multiply(new BigDecimal("100")).intValue() + "";
        // JSAPI下单
        try {
            // 第一步：先构建config
            Config config =
                    new RSAAutoCertificateConfig.Builder()
                            .merchantId(HaloPaymentConstant.MERCHANT_ID)
                            .privateKeyFromPath(HaloPaymentConstant.PRIVATE_KEY_PATH)
                            .merchantSerialNumber(HaloPaymentConstant.MERCHANT_SERIAL_NUMBER)
                            .apiV3Key(HaloPaymentConstant.API_V3_KEY)
                            .build();

            // 第二步：构建service
            JsapiService service = new JsapiService.Builder().config(config).build();
            // request.setXxx(value)设置所需要参数
            PrepayRequest request = new PrepayRequest();
            Amount amount = new Amount();
            amount.setTotal(Integer.parseInt(totalAmount));
            request.setAmount(amount);
            request.setAppid(appId);
            request.setMchid(HaloPaymentConstant.MERCHANT_ID);
            request.setDescription("测试JSAPI下单支付");
            request.setNotifyUrl("http://127.0.0.1/notify");
            request.setOutTradeNo(exist.getCode());
            Payer payer = new Payer();
            payer.setOpenid(user.getOpenId());
            request.setPayer(payer);
            PrepayResponse response = service.prepay(request);

        } catch (Exception e) {
            e.printStackTrace();
            return R.error("小程序付款V3版本!");
        }
        return R.ok("支付成功!");
    }

}
